//
// Created by wangjunjun on 2024/3/21.
//

#include "ftpupload.h"
#include "../log/log.h"
#include "../config/configmanager.h"
#include <experimental/filesystem>
#include <iostream>
#include <fstream>

std::shared_ptr<CFtpUpload> CFtpUpload::m_InStance = nullptr;
std::once_flag CFtpUpload::m_flag;
namespace fs = std::experimental::filesystem;

CFtpUpload::CFtpUpload():m_ftpUrl(""),
                         m_userName(""),
                         m_passWord(""),
                         m_httpPort(9527),
                         m_ftpPort(2121),
                         m_httpInsertUrl(""),
                         m_IsSend(false),
                         m_localPath(""),
                         m_remotePath(""){
    init();

}
CFtpUpload::~CFtpUpload(){
}

std::shared_ptr<CFtpUpload> CFtpUpload::GetInStance() {
    std::call_once(m_flag, [](){
        m_InStance= std::shared_ptr<CFtpUpload>(new CFtpUpload(),
                                                  [](CFtpUpload *ptr){delete ptr;});
    });
    return  m_InStance;
}
/**********************
 * 初始化获取配置文件
 * *********************/
void CFtpUpload::init() {
    //初始化读取配置文件
    std::shared_ptr<CConfigManager> pIntance = CConfigManager::GetInstance();
    if (nullptr != pIntance) {
        std::string strPath = "./CFG/config";
        Json::Value tempValue = Json::Value::null;
        if (!pIntance->loadConfig(strPath, tempValue)) {
            LOG_ERROR("Loading Config error!");
            return;
        } else {
            if (tempValue.isMember("StoreServer") && tempValue["StoreServer"].isObject()) {
                if (tempValue["StoreServer"].isMember("address") && tempValue["StoreServer"]["address"].isString()) {

                    m_address = tempValue["StoreServer"]["address"].asString();
                    LOG_INFO("m_address:{} ",m_address);
                }
                if (tempValue["StoreServer"].isMember("ftpPort") && tempValue["StoreServer"]["ftpPort"].isInt()) {
                    m_ftpPort = tempValue["StoreServer"]["ftpPort"].asInt();
                    LOG_INFO("m_ftpPort:{} ",m_ftpPort);
                }
                if (tempValue["StoreServer"].isMember("httpPort") && tempValue["StoreServer"]["httpPort"].isInt()) {
                    m_httpPort = tempValue["StoreServer"]["httpPort"].asInt();
                    LOG_INFO(" m_httpPort:{} ",m_httpPort);
                }
                if (tempValue["StoreServer"].isMember("userName") && tempValue["StoreServer"]["userName"].isString()) {
                    m_userName = tempValue["StoreServer"]["userName"].asString();
                    LOG_INFO("m_userName:{} ",m_userName);
                }
                if (tempValue["StoreServer"].isMember("passWord") && tempValue["StoreServer"]["passWord"].isString()) {
                    m_passWord = tempValue["StoreServer"]["passWord"].asString();
                    LOG_INFO("m_passWord:{} ",m_passWord);
                }
                m_ftpUrl = "ftp://" + m_address+ ":" + std::to_string(m_ftpPort) + "/";
                m_httpInsertUrl = "http://"+ m_address+ ":" + std::to_string(m_httpPort) + "/" + "insert";//插入httpUrl
                LOG_INFO("m_ftpUrl {}" , m_ftpUrl);
                LOG_INFO("m_httpInsertUrl {}", m_httpInsertUrl);
            }
            if (tempValue.isMember("DogSN") && tempValue["DogSN"].isString()){
                //将DogSn转变路径
                m_localPath = "./"+ tempValue["DogSN"].asString();
                m_remotePath = tempValue["DogSN"].asString();
            }
        }
    }
    //开启发送线程
    return;
}
void CFtpUpload::Start(void){
    m_pSendDataProc = new std::thread(&CFtpUpload::DoSendDataProc, this);
    if(nullptr!= m_pSendDataProc){
        m_pSendDataProc->detach();
    }
    m_pMonitorThread= new std::thread (&CFtpUpload:: GetMitorStatus, this);
    if(m_pMonitorThread!= nullptr){
        m_pMonitorThread->detach();
    }
    return;
}
size_t CFtpUpload::read_callback(void* ptr, size_t size, size_t nmemb, void* stream){
    std::ifstream *file = static_cast<std::ifstream *>(stream);
    if (file->peek() == EOF) {
        return 0;
    }
    file->read(static_cast<char *>(ptr), size * nmemb);
    return file->gcount();
}

void CFtpUpload::uploadFileToServer(const std::string& localFilePath, const std::string& remoteFileName){
    // FILE *file;m_ftpPort
    CURL *curl;
    CURLcode res;

    curl_global_init(CURL_GLOBAL_ALL);
    curl = curl_easy_init();
    if (!curl){
        LOG_ERROR("Failed to initialize CURL");
        return;
    }
    std::ifstream file(localFilePath, std::ios::binary);  // 打开本地文件
    if (!file){
        LOG_ERROR("Failed to open file:{} ", localFilePath);
        curl_easy_cleanup(curl);
        return;
    }

    std::string userpwd = m_userName + ":" + m_passWord;
    const char* userpwd_cstr = userpwd.c_str();
    std::string remoteUrl = m_ftpUrl + remoteFileName;
    curl_easy_setopt(curl, CURLOPT_URL, remoteUrl.c_str());
    curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L);
    curl_easy_setopt(curl, CURLOPT_USERPWD, userpwd_cstr);
    curl_easy_setopt(curl, CURLOPT_READFUNCTION, read_callback);
    curl_easy_setopt(curl, CURLOPT_READDATA, &file);
    curl_easy_setopt(curl, CURLOPT_FTP_CREATE_MISSING_DIRS, 1);
    //设置超时选项
    curl_easy_setopt(curl, CURLOPT_TIMEOUT, 30L); //设置超时时间(秒)
    curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 30L);
    res = curl_easy_perform(curl);
    if (res != CURLE_OK) {
        fprintf(stderr, "curl_easy_perform() failed: %s\n", curl_easy_strerror(res));
        curl_easy_cleanup(curl);
        throw std::runtime_error("Failed to send HTTP request: " + std::string(curl_easy_strerror(res)));
    }
    curl_easy_cleanup(curl);

}

void CFtpUpload::upLoadFiles(const fs::path& localFilePath, const std::string& remotePath) {
    try {
        for (const auto& entry : fs::directory_iterator(localFilePath)) {
            std::string currentLocalFilePath = entry.path().string();
            std::string currentRemoteFileName = remotePath + "/" + entry.path().filename().string();

            if (fs::is_regular_file(entry)) {
                // 尝试上传文件到服务器
                uploadFileToServer(currentLocalFilePath, currentRemoteFileName);

                // 如果上传成功，才发送HTTP请求
                RequestBody reqBody;
                parsePathFile(currentLocalFilePath, reqBody);
                LOG_INFO("msg: {} ", reqBody.msg);
                LOG_INFO("date:{}",reqBody.dateTime);
                LOG_INFO("robotSn:{}",reqBody.robotSn);
                LOG_INFO("position:{}",reqBody.position);
                ResponseBody resBody;
                {
                    std::unique_lock<std::mutex> lock(m_Mtx);
                    if (HttpResquest::GetInStance()->SendRequest(m_httpInsertUrl, reqBody, resBody)) {
                        LOG_INFO("HTTP request successful, resBody.code: {}", resBody.code);
                        if (resBody.code == 200) {
                            // 如果HTTP请求成功且状态码为200，删除文件
                            fs::remove(currentLocalFilePath);
                        }
                    } else {
                        LOG_ERROR("HTTP request failed, not deleting file: {}", currentLocalFilePath);
                    }
                }
            } else if (fs::is_directory(entry)) {
                // 递归处理子目录
                std::string newRemotePath = remotePath + "/" + entry.path().filename().string();
                upLoadFiles(entry.path(), newRemotePath);
            }
        }
    } catch (const std::exception& e) {
        LOG_ERROR("Exception occurred while processing files: {}", e.what());
        // 这里可以添加更多的错误恢复逻辑
    }
}
//解析图片数据
void CFtpUpload::parsePathFile(const std::string& path, RequestBody& requestBody){
    std::vector<std::string> fields;
    std::stringstream ss(path);
    std::string field;
    while (std::getline(ss, field, '/')) {
        fields.push_back(field);
    }
    requestBody.robotSn = fields[1];
    requestBody.dateTime= fields[2];
    requestBody.position = fields[3];
    requestBody.msg= fields[4];
}
void CFtpUpload::handleSignal(int){
    LOG_INFO("Stopping directory monitoring.");
    std::exit(0);
}
void CFtpUpload::GetMitorStatus(){
    CMonitor Monitor;
    CMonitorStatus monitorStatus;
    // 注册观察者
    Monitor.attach(&monitorStatus);
    // 监控指定目录
    LOG_INFO("CFtpUpload::GetMitorStatus  m_address: {} ", m_address);
    LOG_INFO(" CFtpUpload::GetMitorStatus  ftpPort: {} ", m_ftpPort);
    Monitor.startMonitoring(m_address, m_ftpPort, m_localPath);
    // 设置信号处理函数
    std::signal(SIGINT, handleSignal);
    while (true) {
        m_IsSend = monitorStatus.GetMonitorStatue();
        removeEmptyDirectories(m_localPath);
        std::this_thread::sleep_for(std::chrono::seconds(5));
    }
}

void CFtpUpload::DoSendDataProc(){
    while(true){
        if(m_IsSend){
            fs::path localPath = m_localPath;
            upLoadFiles(localPath, m_remotePath);
        }
        std::this_thread::sleep_for(std::chrono::milliseconds (50));
    }
}

//递归删除空目录
void CFtpUpload::removeEmptyDirectories(const std::string& directoryPath) {
    if (!fs::exists(directoryPath) || !fs::is_directory(directoryPath)) {
        LOG_INFO(" directoryPath is not empty!");
        return;
    }
    // 遍历目录中的所有项
    for (const auto& entry : fs::directory_iterator(directoryPath)) {
        if (fs::is_directory(entry)) {
            // 递归处理子目录
            removeEmptyDirectories(entry.path().string());
            // 检查子目录是否为空
            if (fs::is_empty(entry)) {
                // 删除空目录
                fs::remove(entry);
            }
        }
    }
}